{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "jA6jBcLwC580"
   },
   "source": [
    "# 小车上山 MoutainCar-v0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "QPrYUlr7C582"
   },
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "\n",
    "import numpy as np\n",
    "np.random.seed(0)\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "import gym\n",
    "import tensorflow.compat.v2 as tf\n",
    "tf.random.set_seed(0)\n",
    "from tensorflow import keras"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "AhqDEMsUC587"
   },
   "source": [
    "### 环境使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 176
    },
    "colab_type": "code",
    "id": "lWyjPiyHC588",
    "outputId": "2bc6a5e0-9ed2-4faa-c9ea-9a0f504c127a"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "观测空间 = Box(2,)\n",
      "动作空间 = Discrete(3)\n",
      "位置范围 = (-1.2, 0.6)\n",
      "速度范围 = (-0.07, 0.07)\n",
      "目标位置 = 0.5\n"
     ]
    }
   ],
   "source": [
    "env = gym.make('MountainCar-v0')\n",
    "env.seed(0)\n",
    "print('观测空间 = {}'.format(env.observation_space))\n",
    "print('动作空间 = {}'.format(env.action_space))\n",
    "print('位置范围 = {}'.format((env.unwrapped.min_position, \n",
    "        env.unwrapped.max_position)))\n",
    "print('速度范围 = {}'.format((-env.unwrapped.max_speed,\n",
    "        env.unwrapped.max_speed)))\n",
    "print('目标位置 = {}'.format(env.unwrapped.goal_position))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "qwJLWgXlC59B"
   },
   "source": [
    "一直向右，不能成功"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 283
    },
    "colab_type": "code",
    "id": "kdoLX6PVC59D",
    "outputId": "282ef83e-c8ff-4dd0-bf07-d47cb198ccf6"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "失败退出\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x1a319786490>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAD4CAYAAADvsV2wAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAgAElEQVR4nO3dd3xc1Z3w/8/RqIyq1Ysly3KRLLnITcYN9woYMC0xIcEh6xAeNpsnYZMFlvySPNkNIQRI2bQlkACplFAcmrstG9xk3C1LsiXZapZGvZeZOb8/7sg2Qn363PN+veY17erer+6Z+c655557jpBSoiiKovg+P3cHoCiKoriGSviKoig6oRK+oiiKTqiEryiKohMq4SuKouiEv7sDGEhsbKxMS0tzdxiKoihe5dixY7VSyrj+3vPYhJ+WlkZeXp67w1AURfEqQohLA72nmnQURVF0QiV8RVEUnVAJX1EURSdUwlcURdEJlfAVRVF0QiV8RVEUnVAJX1EURSc8th++x+lsgqZyaDNBWy10NIC5E3o6wWoGP3/wM4C/EYKjICQGQqK1xxFjISDY3f+BMpjOZmgqg9Ya6KiH9nroaQdLj3YTfhAYopVjQKj2OCxBK9uwRPAPdPd/oAymqwWaK6G5QvvudjZpr1m6QUqQVvAPgsAwCIqAoHAwRkBEsnbzkfJVCb+vrhaoPK7daoug7iLUFWmJ3h7hSRA1AaLSIGYixE+DxBkwJgWEcEjoyjB0NsGVM1B9Bq6chuqzUF8MnY32rTc0HiLHQewUiJsCcZkQlwGRaeCnDqRdpqNRK9eafKg5p92bzttZvgLCE7XvatQEiM+C+KkQnwljUr2qfIWnToCSk5MjXXKlbVM5FO+Fy4eg4pj2AcG2T0LjIWYyxEzS7iNTISweQuPAGGmr7QVrtXurRavpmzu02mFHg3bfXqtto74EGkqgoRRaqq5t3zgGEmbA2FmQMg/G3aDVGhXHaK6ESx9D6QHtvrbg2nvB0ZA4HWLStbKNHKf9MAdHae8FhoAhCAwBWvmaO6C7HXraoLsNWqqhpfJazbGhFEyF0Hrl2jaCIrSyTZ4LY+dA8hytxqh+5B2jsUz77l4+qN3XnOPq99cYeS0xR6XZautjtbI1Rmhl4x+kHb0htCP27lat0tfVrP14NFdo39+mMm1bdRehufza9gPDIGmmVq7JOZCS4/byFUIck1Lm9Pue7hJ+VyuU7NOS/MU9Wu0dtC958lwt6SbnaAUYEu347YP2gao+B9WntdrmldPazdKlvR+RAuPmwbj52i0xGwzqYGxYLD3al7/gAyj8UKu9g/blTl2g7c+kmZAwXau1OeOL2dEItYVazbLyhFaRqD4L1h7t/bAE7XM2fjGkLdZi8TM4Pg5fZLVo+7PgfSj4EEz52uuBYVplKXWh9j1OmKbtZ2eUb2cTmAq0H5fqs1DxCVw5pTUPgdbEl5Kjfd7SbtS+vy4sX6cnfCHEeuAXgAF4QUr5VJ/3g4BXgLlAHfB5KWXpYOt0aMJvuaIlgIL3oXifllgDQrQv3KQVMHG5VhNwZ63L3K0l/fIjUHYEyo9qtQqwJauFMGGJWz5AHq+zCS7s1Mq4aLv23BAEE5fBxBUwfpHWfObOfdbTqTUjVXwCFXlabbTRNuRJ0BgtOYxfpH0mx87SjioUTXebVjnr/RFvrwVh0PZXxnrtexE/zb2VInOXVnmryIPyPO3721Civdf7/U1bbPv+znRqrE5N+EIIA1AIrAHKgaPAvVLKc9ct8zCQLaV8SAixCbhDSvn5wdZrV8KXUqtdnX9PS/IVx7TXo9Jgyi0wZb1W0/MPGt36XaW5UqutluzXmiR6j0aCxmgf9t4fgIQZXtWO6BANl7Qvf8H72r6xmrUT5RnrYcrN2g95YKi7oxxcU7nWzHTpI1tzU6H2ekCI9vlMuxHSlsDY2T5z0nDYmiuvJfjeSlrQGEhfrZXv5FXaUbknu9qcuB9KP7r2/Q0Mt9X+F0PaUu2I04E/AM5O+AuBH0gp19mePw4gpfzxdctssy1zUAjhD1wB4uQgGx91wm+8DC/fdu3XNXkuTLlJS/TxWd7ddtpcpSW30v3arbe5whhpax640XebCKxWqDpuO1L7QKstA8Rm2Mr3Zq2ZxJv/79aaaz8ApR9BzVnt9YAQrbni6g/AHN/7AZBSaxbpPRKvOqm9HpWmlW3Geq2S481HPi1XbGV7QCvf3vNJQRG2CtxS7RY/za4KnLMT/t3AeinlFtvzLwHzpZRfv26ZM7Zlym3PL9qWqe2zrgeBBwFSU1PnXro04CifA7Na4B//ou24jJsgImmU/5kXaKqwfXhytaOA3iYC4xhIXaQl//GLvfccQHebdq6lcJt2a72inWBLXagl+YybIHayu6N0nra6awni0kfXfuT8g20/ALYjvOQ5nn+02p/udq3iUrhNq8k3VwBC+996j9Tipnh3JW0wrTVa2Zbkarf6i9rrwdGQdSvc9stRrdbZCf8eYF2fhH+DlPLfrlvmrG2Z6xP+DVLKuoHW67JeOr6kqVyrOVw6oH2Qeo8AAsNh/MJrRwFJMz23plRforXDF27TkoGlW4t/0grIvAXS1zrvZLqna6uDy7YeR6UH+vwAzLvuB2Cu5/4A1F3UzrcUbdcqKZYu7bqGSSu0BJ++FsL6nbvD9zVVaJ/5klytOfLmn45qNfpq0lGuaa76dA3xahtxqNYunDz7WnfByFT31KSaKmxt2LY46y5or8ekQ8Y6LQGkLvS9JgxHaK+/1uX06g+A1C7+S5mn1fzHzoakWVrTiFvKt1w7QX3pY+1orbcWGzNZK9v0NdrRaIDR9bH5KGcnfH+0k7argAq0k7ZfkFKevW6ZfwVmXHfS9k4p5ecGW69K+E7QUq0l1ssHtRPZV05f60oWEqslh4Rp2mF07BTtwqGgcMdsW0porb7WBbX6jNaboW9PlUkrIWMtRE90zHb1pL1eK9veaw6u7wpqjNTKd+wsrWxj07WkGxzpmG1LqZ2krMnXzj1cOQ2XD0PTZe39wDDthzt9rXbiVZWv07iiW+bNwM/RumX+QUr5IyHED4E8KeVWIYQR+BMwG6gHNkkpiwdbp0r4LmDu1hJv5Sdad8Heq4t7kwRofYrHJGsXJEUka+dEjJHaIWfvhWeGIK2XjKVHu3ilo0EbnqDNdtFZ42XtoqSO+mvrjUzVmpbGL9ZOWPniiWZ3M3dpfcUrj2vXA1Qe155bzdeWCY3Trh4NT9D6rYclahcXBoVrJ4sDjIDQhh6QFu28SmeTdq1Ba7VWvs0V2tFjZ9O19YYnXesXn7pQK19vPI/khdSFV8rwWXpsV4wWaF1b64u1mltLldZE1NU05CquMgTZLkcfryX4+KnaFz9hmuNqlsrI9JZvbaH2415bpNXCW2u0XiQjGYLAP1irDEQkazX2hGm2K1uz9HuexQMMlvDVT67yaYYA7XA/Nh2yNnz2/a5W7bLzng5tcLGeDq1W7xeg/a0hUEvmITFaDdFXe1h4q+vLtz/mLi35d7ddK1+kdqGT8NOO7IIjrw0wpsrXq6iEr4xMUJh2U3yTf5A2ppDik3R2eaaiKIp+qYSvKIqiEyrhK4qi6IRK+IqiKDqhEr6iKIpOqISvKIqiEyrhK4qi6IRK+IqiKDqhEr6iKIpOqISvKIqiEyrhK4qi6IRK+IqiKDqhEr6iKIpOqISvKIqiEyrhK4qi6IRK+IqiKDqhEr6iKIpOqISvKIqiEyrhK4qi6IRK+IqiKDqhEr6iKIpO2JXwhRDRQogdQogi233UAMt9KIRoFEK8a8/2FEVRlNGzt4b/GLBLSpkO7LI9789PgS/ZuS1FURTFDvYm/NuBl22PXwY29reQlHIX0GLnthRFURQ72JvwE6SUVQC2+3h7ViaEeFAIkSeEyDOZTHaGpiiKolzPf6gFhBA7gcR+3nrC0cFIKZ8HngfIycmRjl6/oiiKng2Z8KWUqwd6TwhRLYRIklJWCSGSgBqHRqcoiqI4jL1NOluBzbbHm4F37FyfoiiK4iT2JvyngDVCiCJgje05QogcIcQLvQsJIfYDrwOrhBDlQoh1dm5XURRFGaEhm3QGI6WsA1b183oesOW650vs2Y6iKIpiP3WlraIoik6ohK8oiqITKuEriqLohEr4iqIoOqESvqIoik6ohK8oiqITKuEriqLohEr4iqIoOqESvqIoik6ohK8oiqITKuEriqLohEr4iqIoOqESvqIoik6ohK8oiqITdg2PrLhHU3sPbx0vZ9f5GioaO5ASMhPDWZ2VwK0zxxLor37HvVlrl5nX88rILTRxwdSK0d/A+JhQ1k9PZEN2EsYAg7tDVOzQ0tnDW8cr2JlfQ3lDO1arZEpiOCsz49k4O5kgf+eVr5DSM6eOzcnJkXl5ee4Ow6NIKfnL4cs8s72AxvYe0uPDyEgMR0rJqfImyhs6SBpj5Cd3ZbM0I87d4SojJKXk9WPl/Ne752jpNJMeH0ZmUgQ9ZiunK5qoaOwgOTKYH9w2jTVTE9wdrjJCUkpezyvnJx+ep66tm4mxoWQlRQBwqqKRsvoOEiKC+NHGGay2o3yFEMeklDn9vqcSvnfo7LHw2D9O8faJShZOjOGJW7KYnjzm6vtSSvYVmvjRe/kU1bTybysn88iaDIQQboxaGa5us5XvvHGSd05UMn9CNI/fnMWscZFX35dS8tGFOv7r3XMUVLfwjZWT+ebqDPz8VPl6gy6zhf/v7TO8llfODWnRPHFLFjP7Kd8n38/nXFUzmxeO5/u3ThtV+aqE7+W6zBa2vJzH/qJavrNuCg8vnzRgIu/ssfC9d7QP1pcXpfH9W6eqpO/hOnssPPyXT9h9voZvr83g4eWTB/yid5utfPft07yWV84X5qfyo43TVfl6uB6Llf/z50/YmV895A91l9nCTz4ooKPHzI/vzB7V9gZL+KoN38NZrZJHXj3J/qJanr47m8/ljBt0eWOAgZ/clU24MYAXD5QQGxbI11emuyhaZaSklDz+5ml2n6/hR3dM57754wddPtDfj5/clU1UaCD/u6+Y2NBAHlk7xUXRKiMlpeTRN06xM7+aH94+jfsXpg26fJC/ge/dOhWr1TkVcZXwPdxv913kvdNV/OfNmUMm+15CCL57SxZ1rV08s72Q9IRw1k1LdHKkymj8dt9F3jpewb+vyRgy2fcSQvDY+kwa2rr55e4LTB0bwfrpSU6OVBmNP35UypvHK/jW6owhk/31nNVUp7pzeLDDxXU8u72ADdlJfHXJxBH9rRCCp+7KZmbKGL7z+kmuNHU6KUpltE6UNfLs9kJuyU7i6ysnj+hvhRD898YZWvm+cYqy+nYnRamM1vHLDTz5fj6rsxL4xqqRla+zqITvoTq6LXz7jZOkRofw4ztnjKqd1hhg4BebZtNjkXznjZN46vkaPerotvDIqydIjDCOunwD/f341RfmAKjy9TBdZgvfeeMU8eFBPHvPTI85z6ISvof62c5Cyuo7eMrWHj9aabGh/OctWewvquUfn1Q4MELFHr/cXURxbRs/vSebCDvKd1x0CI/flMWh4npeP1buwAgVe/x69wUu1LTy5J0zGBMy+vJ1NJXwPVDBlRZe2F/MvTeMY8HEGLvXd98NqcxOjeSpD87T3NnjgAgVexSbWnlhfzF3zUlh0aRYu9e3ad445qVF8aP38mlo63ZAhIo9Smrb+O2+i9wxO5nlU+LdHc6n2JXwhRDRQogdQogi231UP8vMEkIcFEKcFUKcEkJ83p5t6sGT7+cTFuTPo+szHbI+Pz/BD2+bTl1bF7/cWeSQdSqj98N3z2EMMPDYTY4r3//eOIOWzh5+teeCQ9apjN5TH+QTaPDj8ZsdU76OZG8N/zFgl5QyHdhle95XO3C/lHIasB74uRAisp/lFGB/kYl9hSb+bWU6kSGBDlvvjJQx3DM3hVcOXqKiscNh61VG5uDFOvYWmPi3lZOJCw9y2HqnJIZzz9xxvHKwlMt16gSuuxwpqWfb2WoeWjaJ+HCju8P5DHsT/u3Ay7bHLwMb+y4gpSyUUhbZHlcCNYC67r8fUkp+uq2AlKhg7l80vC56I/F/V2cA8D+7VC3fHaSUPLO9gMQI44i66A3XI2sz8Pfz47kdBQ5ftzI0KSXPbCsgISKILSPsVecq9ib8BCllFYDtftAGKyHEDUAgcHGA9x8UQuQJIfJMJpOdoXmfvQUmTpU38Y2V6U4ZQCk5MpgvzE/l9WPllNS2OXz9yuD2Fpo4dqmBf1s12SkDoCVEGPniglS2nqxU5esGh4rrOVJaz8PLJxMc6JkD3A2Z8IUQO4UQZ/q53T6SDQkhkoA/AQ9IKa39LSOlfF5KmSOlzImL09dBgJSSX+wqIjkymDvmJDttOw+vmITBT/B8br+/uYoT/XbPRZIjg4d9Ad1ofHXpRAIMfvxGteW73C92FRIfHsTn5zmvfO01ZMKXUq6WUk7v5/YOUG1L5L0Jvaa/dQghIoD3gO9KKQ858h/wFQcv1nGirJGHV0wiwOC8zlPx4UbumZvCP45VUNOsLsZylbxSrfa3ZckEp5fvvTek8ubxCsobVFu+q3xyuYFDxfV8bdkkjx6+2t5P3lZgs+3xZuCdvgsIIQKBt4BXpJSv27k9n/WCbdybu+akOH1bDy6diNlq5Q8flTp9W4rmd/suEhUS4JLa31eXau3HL39c6vRtKZoX95cQYfRnkwfX7sH+hP8UsEYIUQSssT1HCJEjhHjBtszngKXAl4UQJ2y3WXZu16dcNLWy+3wNX1ww3iW1g/Exodw0I4m/HL5Ee7fZ6dvTu5LaNnbm1/ClhWmEBDp/+KrkyGDWT0/k70fKaO1S5etsZfXtfHCmii/MH09okGcPT2ZXwpdS1kkpV0kp02339bbX86SUW2yP/yylDJBSzrrudsIRwfuKP35UQqC/H19c4PieOQN5YFEaLZ1m3j5e6bJt6tUrB0vx9xN8cX6qy7a55cYJtNhmzlKc6+WPS/ETgs1O6FnnaOpKWzdr6zLz1icV3Jo9ltgwx/XLHsrc8VFMTYrglYOlagwWJ2rrMvNGXjk3z0giPsJ1/bJnp0YxOzWSPx26pMrXibrMFv7xSTnrpiWSNCbY3eEMSSV8N3vvdBVt3RbuvcG1bX/CViM5f6WFIyX1Lt22nrx1vIKWLjObF6W5fNv3zR9PsamNQ8WqfJ1l+9lqGtp72OTi7+9oqYTvZq8eLWNSXChzx39mVAqnu21mMuFB/ryWpwbdcpZXj5aRlRTBnFTXX1y+ITuJCKM/fzl8yeXb1ou/H71MSlQwix0wJpIrqITvRoXVLRy71MCmealuGT41ONDAhplJvH+6Sp3cc4IzFU2crmhi07xxbilfY4CBu+amsO3sFWpbu1y+fV93ua6djy7U8fmccV4zt7BK+G706tEyAgzCqRdaDeXuuSl09Fh4/3SV22LwVa/llRHo78fGWe4r303zUumxSP55Up2cd7TX8srwE3B3jvO7UjuKSvhu0mW28OYn5ayZmuDSk7V9zUmNYmJsKG+osdQdqrPHwtvHK7hpeqJbx0OfkhjO1KQI3jqu5kJwJLPFyuvHylgxJd4rTtb28uxOoz5sxzntZM/n57muq15/hBDcNTeFn24r4FJdG+NjQt0aj6/YW2CiudPskgvphnLnnGT++718LtS0Mjk+zN3hDKmnp4fy8nI6Oz33SvDOHgv/vTyamLBA8vPz3RKD0WgkJSWFgIDhVyhUwneTV4+WkRwZzI2T3X+y5845yTyzvYB/fFLBI2sy3B2OT9h6soLYsEAWTbJ/Aht73TZzLE++n8/bxyv49rop7g5nSOXl5YSHh5OWluYxUwP2VVrbRlCPhczEcPzcEKOUkrq6OsrLy5kwYcKw/0416bhBbWsXH12oZePssRg84GRP0hjth+cfx8qxWlWfbXs1d/awM7+GDdlj8XfiuDnDFR9h5Mb0ON4+UeEV5dvZ2UlMTIzHJnuL1UpLl5nI4AC3JHvQjsxjYmJGfBTk/k+jDn1w5gpWCbfOHOvuUK66e24KFY0dHCquc3coXm/bmSt0m63cNstzyveO2WMpb+gg71KDu0MZFk9N9gBNHWaklEQGu3eu2tHsI5Xw3eCfJyuZHB/GlIRwd4dy1bppiYQGGvjnKdWbw15bT1aSGh3C7HGeM7HbummJhAQaeOu4Ojlvr6aOHgINfkOOef+73/2OV155BYCXXnqJyspr360tW7Zw7tw5p8bZH5XwXay6uZOjpfXcmj3Wo2oxxgADq7IS2Ha2GrOl3+kKlGGoaenkowu13D7Ls8o3JNCf9dMSefdUFZ09FneH47XMFiutnWbGhAQMWb4PPfQQ999/P/DZhP/CCy8wdepUp8baH5XwXey9U1VICRtmJrk7lM+4eUYS9W3d6lJ8O7x7sgqrhNs9qDmn18bZybR0mtlXqL/Z5EaqtLSUzMxMNm/eTHZ2NnfffTft7e3884Nt3LN+CWsWz+MrX/kKXV3aBW2PPfYYU6dOJTs7m29/+9sA/OAHP+CZZ57hjTfeIC8vj/vuu49Zs2bR0dHB8uXLycvLA+Bvf/sbM2bMYPr06Tz66KNXYwgLC+OJJ55g5syZLFiwgOrqarv/L9VLx8X+eaqSqUkRTIrzvO5xy6fEERJo4L3TVdyY7v7eQ97onRMVTE2KYHK85zTX9Vo4KYbIkAC2nbnCummJ7g5nWP7fP89yrrLZoeucOjaC7986bcjlCgoKePHFF1m8eDFf+cpXeO655/j1b3/HH1/byrpFs9m8eTO//e1vuf/++3nrrbc4f/48QggaGxs/tZ67776bX/3qVzzzzDPk5OR86r3KykoeffRRjh07RlRUFGvXruXtt99m48aNtLW1sWDBAn70ox/xH//xH/z+97/nu9/9rl3/u6rhu1B5QzvHLzd6ZO0etGadlZnxbDt7RTXrjEJZfTsny5s86mTt9QIMfqzOSmBHfjXdZlW+Qxk3bhyLFy8G4Itf/CI7d+5ibEoqs2ZM1QYf3LyZ3NxcIiIiMBqNbNmyhTfffJOQkJBhb+Po0aMsX76cuLg4/P39ue+++8jNzQUgMDCQDRs2ADB37lxKS0vt/p9UDd+F3julDV+wYYZnJgSAW2Yk8e6pKo6U1LPIA64R8Cbbzl4BYL0H155vmp7IG8fKOVhcx7IMz583ejg1cWfp20Zvtmo/kn175/j7+3PkyBF27drF3//+d371q1+xe/fuYW1jsKGrAwKunScwGAyYzfaPd6Vq+C707qkqZqaMITVm+DUAV1s+JZ7gAK1ZRxmZ7WermZIQTlqs516tvHhyLKGBBj48o8p3KJcvX+bgwYOA1s4+f/FyqirKKL9UAsCf/vQnli1bRmtrK01NTdx88838/Oc/58SJz87vFB4eTktLy2denz9/Pvv27aO2thaLxcLf/vY3li1b5rT/SSV8F6ls7OB0RRM3zfDM5pxewYEGVmZpzToWL7hIx1PUtnZx9FI966YluDuUQRkDDKzMSmD72WpVvkPIysri5ZdfJjs7m7q6Oj7/la/xy988zz333MOMGTPw8/PjoYceoqWlhQ0bNpCdnc2yZcv42c9+9pl1ffnLX+ahhx66etK2V1JSEj/+8Y9ZsWIFM2fOZM6cOdx+++3O+6eklB55mzt3rvQlL39cIsc/+q68UNPi7lCG9N6pSjn+0XflRxdM7g7Fa/z9yCU5/tF35enyRneHMqR3T2rle/BirbtD6de5c+fcHYIsKSmR06ZNu/q8rrVLnixrkO1dPW6M6rP621dAnhwgr6oavovsOFfNxLhQj+yd09fyKXEYA/zYduaKu0PxGtvOVpMcGcy0sRHuDmVIy6fEEeTvx4eqfIet2XaxlTFg8IutPJ1K+C7Q1NHDwYt1rJnq2Yf7vUIC/blxciw782vUfKjD0Npl5kBRLeumJXrUxVYDCQ3yZ2lGHNvOXvGKsXXcIS0tjTNnzgBgtUpau8yEBw99sZWnUwnfBfYW1GC2StZ6ScIHWJ2VQEVjBwXVnz3RpHza3oIaui1Wj2+/v95N0xOpaurkZHnj0AvrXEuXGauUjDF6f6dGlfBdYMe5amLDApk1zvXz1o7Wysx4AHaes//qPl+37Ww1MaGB5KRFuzuUYVuVmYDBT7Arv8bdoXi85o4eDH6CkCCdJ3whRLQQYocQosh2/5mMJoQYL4Q4JoQ4IYQ4K4R4yJ5teptus5V9BaarXzBvER9hZGbKGHaqhDCobrOVPedrWJ3lXeU7JiSAueOj2H1ele9gpJS0dJoJN7pvKGRHsreG/xiwS0qZDuyyPe+rClgkpZwFzAceE0J47pVHDnaouI6WLrPXtN9fb3VWAifLG6lp8dyZh9ztSEk9rV5avqsy4zlX1UxVU8fQC+tUe7cFs9VKhA8054D9Cf924GXb45eBjX0XkFJ2Sym7bE+DHLBNr7LjXDXBAQavHJtmVVYCUsIeVQsc0O7zNQT6+7FosvtnthqpVVlas52q5Q+suaMHIQThgyT8sLDR9bz73ve+x86dOwH4+c9/Tnt7+6jWMxL2Jt8EKWUVgO0+vr+FhBDjhBCngDLgJ1JKXQy6LqVkV341S9JjvbI7V1ZSOMmRwapZZxB7CmpYODGGkEDvqwFOigsjNTqE3ap8B9TcaSY00IDBz/H11B/+8IesXr0a8KCEL4TYKYQ4089t2JeDSSnLpJTZwGRgsxCi3+NfIcSDQog8IUSeyeT9Q7gW1bRS2dTJisx+fwc9nhCCVVnxHCiqVWOo96Okto2S2rarJ7i9jRCClZnxHLhQS0e3Kt9ejz76KL/5zW/oNlvoMlv47XNP8eyzz/LTn/6UefPmkZ2dzfe///3P/J2Uku985ztMnz6dGTNm8Oqrr1597+mnn2bGjBnMnDmTxx7TWr6//OUv88Ybb/DLX/6SyspKVqxYwYoVK3jxxRf51re+dfVvf//73/PII4845H8bsloipVw90HtCiGohRJKUskoIkQQMWlWQUlYKIc4CS4A3+nn/eeB5gJycHK/vIJxrG3d8qRcMUjWQ1VkJvHLwEh9frGVlpve1UztTb1OItyZ80GJ/6eNSDhZ7aPl+8BhcOe3YdSbOgJueGvDtTR/+76MAACAASURBVJs28c1vfpPP3/8vALz79ps8/vhjHDhwgCNHjiCl5LbbbiM3N5elS5de/bs333yTEydOcPLkSWpra5k3bx5Lly7lxIkTvP322xw+fJiQkBDq6z8938Q3vvENnnvuOfbs2UNsbCxtbW1kZ2fz9NNPExAQwB//+Ef+93//1yH/ur3HKVuBzbbHm4F3+i4ghEgRQgTbHkcBi4ECO7frFfYVmpgUF0pyZLC7Qxm1+ROjCQ00qO57/dhbUMPk+DDGRXvuYHhDmT8xmhBVvp8ye/ZsampquFhaxsXzZ4mOjuLUqVNs376d2bNnM2fOHM6fP09RUdGn/u7AgQPce++9GAwGEhISWLZsGUePHmXnzp088MADV4dNjo4evPtuaGgoK1eu5N133+X8+fP09PQwY8YMh/xv9jY8PgW8JoT4F+AycA+AECIHeEhKuQXIAp4VQkhAAM9IKR38k+15OnssHCmp5wvzU90dil2C/A0smhzLvkITUkqvv9LQUdq6zBwurufLi9PcHYpdgvwNLEmPZff5Gs8s30Fq4s5011138dab/6ClsZZNmzZRWlrK448/zte+9rUB/2agq9JHs1+3bNnCk08+SWZmJg888MCI/nYwdtXwpZR1UspVUsp023297fU8W7JHSrlDSpktpZxpu3/eEYF7usMl9XSZrV7dnNNrWUYc5Q0dlNS2uTsUj3HgQi3dFisrpnhvc06vVZkJVDV1kl+lrqrudftd9/DB1n/w4T/f5u6772bdunX84Q9/oLW1FYCKigpqaj59VLR06VJeffVVLBYLJpOJ3NxcbrjhBtauXcsf/vCHqydl+zbpwGeHT54/fz5lZWX89a9/5d5773XY/+V9XQu8RG6hiUB/PxZM8L7uen31TpSxr9DERC8Y/M0V9pyvITzIn5w077l6eiDLM7Xy3VNQw1QvGPzNFcZNnEJbaxspKSkkJSWRlJREfn4+CxcuBLSumH/+85+Jj7/2g3/HHXdw8OBBZs6ciRCCp59+msTERNavX8+JEyfIyckhMDCQm2++mSeffPJT23vwwQe56aabSEpKYs+ePQB87nOf48SJE0RFOe4zJjx1cKycnBzZO8mvN1rz3D4SIoz8ect8d4fiECuf3UtqdAgvPXCDu0NxOyklC368i7njo/jNfXPdHY5D3PyL/YQb/Xn1awvdHQr5+flkZWW5NYYLNS2AYHK8+yo4GzZs4Fvf+harVq0acJn+9pUQ4piUMqe/5XV1EZSrVDZ2UFTTytIM77vYaiDLMuI4VFynumcC+VUtVDd3sdwHmnN6Lc2I49ilBlq77J9Gz9uZLVbauy2DXmzlTI2NjWRkZBAcHDxosh8NlfCdYH+R93fH7GtZRhydPVaOlHy2/VFvcm3l6w1zwg7X0oxYzFbJxxdq3R2K2/X+6IW7abC0yMhICgsLef311x2+bpXwnSC3sJaEiCCmJIS7OxSHWTAxhiB/P/YVev8FcfbKLTSRmRhOQoTR3aE4TM54rXtm74+ZnrV0mjH4CYIDve/q+KGohO9gFqvkwIValqTHeV4XNzsYAwzMnxij+4Tf3m0mr7TBp47eAG08oEkx5BZ6Rg3fXecWpdQmOwkL8vf47+9o9pFK+A52sryRpo4en0sIoDVhXKhppbzB+WN+eKpDxXV0W6wsTfe98l2aEcfl+nZK3dz91mg0UldX55ak32m20mOxEm4McPm2R0JKSV1dHUbjyI4yVbdMB8stNCEELJnsOydsey3LiOO/0JqsvP2CstHKLazFGODnE90x++r9EdtXaCItNtRtcaSkpFBeXo47xtNq6eyhqcOMX5ORag+f38BoNJKSkjKiv1EJ38FyC01kp0QSFRro7lAcrneYiH2FNfpN+EUm5k+I8crRT4eSFhtKanQIuYUmNi9Kc1scAQEBTJgwwS3b/uILh6lp6WT7t+a4ZfvOppp0HKipvYcTZY0s88Kx74dDCMGyKXF8dKGOHovV3eG4XHlDO8WmNp9sruu1LCOOg8V1dJn11/22vdvMkZJ6n+p91ZdK+A504EItVulb3TH7WpYRR2uXmU8uNbg7FJfrPaG5zIeur+hraUYc7d0WjpXqr3wPF9dr52d8+PurEr4D5RaaCDf6M2tcpLtDcZpFk2Lw9xO67K2TW2hi7Bgjk3x4eImFveWrw+6Z+wpNGAP8mOdFk9GPlEr4DiKlJLfIxOJJsfgbfHe3hhsDmDM+SncJ32yx8tFF3+tu21dYkD9zx0d5TPdMV/Ll8zO9fDczudiFmlaqmjp9+nCw17KMOM5WNutqcvOT5Y20dJp1Ub5LM+LIr2qmplk/5auH8zOgEr7D7Ls6u5Xvtu/26j2pdaBIP7XAfYW1+Am40Qe72/bVW765OipfPZyfAZXwHSa3qJaJcaGkRHnv7EfDNTUpgpjQQPbrKiGYmDkukjEhnn1BjiNMTYogNizw6hSdeqCH8zOgEr5DdPZYOFxc55NXX/bHz09wY3os+4tqsVo9c3htR2ps7+ZUeSNLdFS+S9Lj2F9k0kX59p6fWZrh2+dnQCV8hzhim93Kl/vv9rUkPY7a1i7OX/H9WZI+ulCHVfr+4f71lmbE0tDew5nKJneH4nQnyvRzfkYlfAfILTQRaPBj/kTf7c7V1xLbxWX7ddB9r7e77cwU3+1u29eNk7Xkp4dmu9xCE34CFk/y/R90lfAdILfIxLwJUYQE6mekioQII1MSwn1+OF0pJft10N22r7jwIKYmReii++2+olpm6eT8jH4+wU5S1dRBYXWrbtrvr7ckPZajJQ10dPvuZfgXTW1U6qS7bV9LM+L4xMdnwWpo087P6KV8VcK3035bdy69fGCutzQjjm6LlcMlde4OxWl6e6os8dHxkQazNF2bBevgRd8t3/0XapHSt2YvG4xK+HbaV2QiPjyIzETfmd1quG6YEE2gv59Pt/PuLzIxMTaUcdG+3922r7lpUQQHGHz6PE1uoYnIkACydXJ+RiV8O1iskgNFvn+5/UCMAQbmT4j22YTQZbZwqLhel7V7gCB/AwsmRvvsD/rV8zOTYzF4+Nj3jmJXwhdCRAshdgghimz3A84KIYSIEEJUCCF+Zc82Pcmpq7Nb6TMhgNbUUVjdypUm37sM/9ilBjp6LLrpf9+fpRlxlNS2UVbve7OcFVS3UN3cxTIdla+9NfzHgF1SynRgl+35QP4L2Gfn9jxKbmGtNruVjj4wffX+775Yy88trMXfT7BgUoy7Q3Gb3vL1xd5YV8/P6KjCZm/Cvx142fb4ZWBjfwsJIeYCCcB2O7fnUXKLTGQnjyHaB2e3Gq7MxHDiwoN8ctyV/UUm5o6PIixIP91t++qd5cwXh1nILawlIyGMpDHB7g7FZexN+AlSyioA23183wWEEH7As8B3hlqZEOJBIUSeECLPHfNZjkRTew/HLzfosnfO9YQQLEmP5YCPXYZf29rF2cpmVb628v34Qh1mH5rlrKPbwpHSet11px4y4QshdgohzvRzu32Y23gYeF9KWTbUglLK56WUOVLKnLg4zy6Ijy76/uxWw7U0PY6G9h7OVja7OxSH+eiCdsSi1xO211uaEUdLl5kTZY3uDsVhDpXU0W327dmt+jPksaqUcvVA7wkhqoUQSVLKKiFEElDTz2ILgSVCiIeBMCBQCNEqpRysvd/j5RaaCA/y7dmthmuxbcjg3CITM1LGuDkax9hXaCIqJIBpY33j/7HH4kmx+AltRNgcH5kNKrfQRJC/HzdM8I3/Z7jsbdLZCmy2Pd4MvNN3ASnlfVLKVCllGvBt4BVvT/ZSSnILTSyaHEOAji63H0jvZfi+cuJW665Xy43pcbrprjeYMSEBzBwX6VPt+LmFJuZP9O3Zrfpjb7Z6ClgjhCgC1tieI4TIEUK8YG9wnuqiqVW3l9sPZElGLMcuNdDmA5fhF1S3YGrpUs0511mSHsep8kYa27vdHYrdKho7uGhqY6kOy9euhC+lrJNSrpJSptvu622v50kpt/Sz/EtSyq/bs01PsK93OAWdnfAZzLL0OHoskkPF3n8Zfu9wGSrhX7MsIxar1IaK9na9RyrLp+jv+6vaI0Yht1C/l9sPZG5aFMYA3xhmIbfIRHq8vrrrDWVmSiThRn+faLbTy+xW/VEJf4Q6eywcLqlTzTl9aJfhx3j9BTpa+dar8u3D3+DH4kmx5BaakNJ7u9+aLVYOXNDH7Fb9UQl/hI6W1tPZY9X1cAoDWZIeR7GpjfIG770M/0hJPd1mq2rO6ceSjFgqmzq5aGpzdyijpqfZrfqjEv4I9c5utWCifi+3H0jvSbADXtyss7/INnvZBFW+ffWes/Lm3jp6mt2qPyrhj1BuYS05afqa3Wq4JseHkTTG6NXt+LmFtcybEEVwoL666w3HuOgQJsSGenU7vp5mt+qPSvgjcKWpk4LqFt0eDg7l6jALF2qxeOEwC9XNtvJVva8GtDQ9lkPF9XSZvW+WM73NbtUflfBHYF+hdiGxHrtzDdeS9DiaOno4Ve59l+H3HpnoefTToSxJj6Ojx8Kx0gZ3hzJiuUUmpITlUz4z5JduqIQ/AnvOm0i0Td6t9G/x5FiEwCubdfYXmYgN0+fsZcO1cFIMAQbBPi9s1tlbYCI6NJDsZP0Ol6ES/jD1WKx8dKGWFZn67M41XNGhgcxIHuN1J/YsVm04hSXpsfip4RQGFBrkz5zUqKsXp3kLq1Wyr9DEsow4XZevSvjDdOxSAy1dZpZl6PdwcLiWZ8TxyeUGr7oM/2R5I/Vt3azIVOU7lKUZcZyrasbU0uXuUIbtVEUT9W3dum+OVQl/mPYU1ODvJ1g8WXXXG8qKzHisUhtx0lvsPV+Dn0CX46uMVO9J7QMXvKh8C2p0PzsdqIQ/bPsKTMxLiybcqM/uXCORnRJJdGggewu8JyHsKdBmt4oM0e/sZcM1bWwEMaGB7POi8t1bYGKm7XOpZyrhD0NVUwfnr7To/nBwuAx+guUZcewtqPGK7pk1zZ2crmjSde+NkfDzEyybEsfeQpNXlG9daxcnyxtZocpXJfzh6K2pqoQwfMsz42lo7+GkF3TP3GtrelIJYfhWZSbQaJvm09PtL6q1dcdUFTaV8Idhb0ENY8cYyUjQ3+h6o7UsPQ4/AXvO9zcJmmfZc76GxAgjWUmqO+ZwLcmIxd9PsMsLyndvQQ0xtt5jeqcS/hC6zVY+ulDHsinxqjvmCIwJCWDu+Ch2e3hC6LFY2V+kutuOVIQxgBsmRLM737PL12KV5BbV6r47Zi+V8IeQV1pPa5eZFepwcMRWZMZztrKZ6uZOd4cyoKNXy1c154zUysx4CqpbKKv33NFRT5Q1aN0xVXdbQCX8IW0/V02Qvx83qu56I7bS9iXbW+C5tcC9BdromL0TsSvDtyorAdC6LHuq7eeq8fcTLNPx+DnXUwl/EFJKdpyrZkl6rBodcxSmJISTNMbo0c06u8/XMH9iNKFBqnxHakJsKBNjQ9nlwc06O85Vs2BiDGOCVXdqUAl/UPlVLVQ0drDaVpNRRkYIwcrMePYX1dLZ43mjKxabWrlQ08oqdbg/aisz4zl4sc4jJ6+/aGql2NTGmqnq+9tLJfxB7DhXjRDXDl2VkVs3LZH2botHToqy7Ww1AGunJbo5Eu+1Miuebts4U55m5zmtfFerhH+VSviD2JF/hdnjIokLD3J3KF5rwcQYwo3+bDt7xd2hfMa2s1eYkTyGsZFqsvLRmpcWTXiQPzvzq90dymfsOFfN1KQIklX5XqUS/gAqGzs4U9HMmqmq9mePQH8/VmXGszO/GrPF6u5wrrrS1MmJskbWTVO1P3sEGPxYkRnPjnOeVb51rV0cu9ygmnP6UAl/AL01FvWBsd+6aYk0tPdw1IMmzdhhK991qjnHbjdN18r3SEm9u0O5atf5GqRU39++7Er4QohoIcQOIUSR7T5qgOUsQogTtttWe7bpKjvOVTMxNpTJ8erqWnstmxJHkL+fRzXrbD97RZWvgyyfEk9wgIEPznhO+e44V83YMUamjY1wdygexd4a/mPALillOrDL9rw/HVLKWbbbbXZu0+maO3s4VFynagcOEhLoz5L0OHacq0ZK9w+21dTew8GLdaydlqiurnWA4EADy6fE8eHZK1g9YDC1zh4L+4tMrJ6aoMq3D3sT/u3Ay7bHLwMb7VyfR9iVX02PRbJWte86zLppCVTYzou42+6CasxWqdrvHeimGUmYWrR2c3fbc76Gzh6raq7rh70JP0FKWQVgux+oQ7NRCJEnhDgkhBjwR0EI8aBtuTyTyX1jbf/zZBVjxxiZPa7fFiplFFZnJWDwEx7RrLPtTDXx4UHMTIl0dyg+Y2VmPIH+frx/usrdobD1ZCWxYUEsmKgmK+pryIQvhNgphDjTz+32EWwnVUqZA3wB+LkQYlJ/C0kpn5dS5kgpc+Li3HMpdENbN7mFJm6dOVYNtuRAUaGBzJ8QzQdnqtzarNPWZWZfoYm10xJU+TpQWJA/S9Pj+PCMe5t1Wjp72H2+hg3ZSRhU+X7GkAlfSrlaSjm9n9s7QLUQIgnAdt/vNdZSykrbfTGwF5jtsP/AwT48ewWzVXLrzLHuDsXn3JKdxEVTG+eq3Ness+NcNR09Fm6bmey2GHzVTdMTqWrqdOscCDvzq+kyW7l1ZpLbYvBk9jbpbAU22x5vBt7pu4AQIkoIEWR7HAssBs7ZuV2n2XqikomxoersvhPcMiOJAIPg7eMVbovhreMVJEcGkzNeNdc52uqsBAIMgg/d2FvnnyerSI4MVs2xA7A34T8FrBFCFAFrbM8RQuQIIV6wLZMF5AkhTgJ7gKeklB6Z8GuaOzlUUseGmWPV2X0niAwJZMWUeN45UemWqfFMLV0cuFDL7bNUc50zjAkJ4MbJsbx7qsotzTqN7Vpz7IbsJFW+A7Ar4Usp66SUq6SU6bb7etvreVLKLbbHH0spZ0gpZ9ruX3RE4M7w3ukqpITbVHOO02ycnUxNSxcHL9a5fNvvntJ+aDbOVs05zrJxdjIVjR0cLXX9RVgfnlHNsUNRV9peZ+vJSqYmRaiLcZxoZWY84UH+vOWGZp23T1SSlRRBRoKaytBZ1kxNICTQwNsnXF++W09WMkE1xw5KJXybsvp2jl9uVLUDJzMGGLh5RhIfnqmio9t1QyaX1LZxsqyRO2ar8nWmkEB/1k9L5N1TVS4dErumuZNDxXXcmp2kmmMHoRK+zZufVCAE6uy+C2ycnUxbt+XqeDau8PZxrXxV7xzn2zg7mZZOs0snRnnzeAVWCbfNUuU7GJXwAatV8vqxMhZPiiUlKsTd4fi8+ROiSRpjdFlvHSkl75yoYOHEGBLHGF2yTT1bPDmWsWOMvJpX5pLtSSl5La+MnPFRqjl2CCrhAx9frKO8oYPPzxvn7lB0wc9PcPusZPYVmlwywfnhknpK69q5Q52sdQmDn+CenHHsLzJR3uD8Cc6PXWqg2NTG59T3d0gq4QN/P3qZyJAANXaOC22aNw6LVfLqUefXAv96+DIRRn82ZKv2e1e5JycFgNfzyp2+rVePlhEaaOCWGao5dii6T/g1LZ1sO3uFO2enEORvcHc4upEWG8qS9Fj+duSyUyfOqG3t4oMzVdw1N4XgQFW+rpISFcKS9DhePVpGjxPLt6Gtm60nK7ltVrKaiH4YdJ/w/3r4Mj0WyZcWjnd3KLpz3/zxVDV1svu8807uvZ5XTo9Fct/8VKdtQ+nf5oXjudLcyfazzjs5/2peGV1mK5sXqe/vcOg64Xebrfzl8GWWT4ljQmyou8PRndVZ8SSNMfLHj0qdsv4ei5VXDpayaFIMk+NV33tXWz4lntToEF76uMQp67dYJX86eIkFE6PJTFR974dD1wn//dNVmFq62Lwozd2h6JK/wY8HFqdxsLiO0+VNDl//e6eqqGrq5KtLJjp83crQDH6C+xeO52hpg1PKd/vZK1Q0drB5YZrD1+2rdJvwrVbJb/ZeICMhjGXp7hmKWYFNN6QSFuTP7/cXO3S9Ukp+v7+YSXGhLMtQ5esun5s3jnCjP7/Ze8Gh65VS8pu9F0mLCWGtmuhk2HSb8HfkV1NY3crDyyergZbcKMIYwL03jOO901WU1rY5bL17C02crWzmq0smqvJ1owhjAJsXpvHh2StcqGlx2Hr3F9VyuqKJh5ZNUuPej4AuE76Ukl/vuUBqdAgbslVXLnf76tKJBBgEv9hV5JD1SSn52Y5CUqKCuXNOikPWqYzeV26cgNHfwK/3XHTI+qSU/M/uIhIjjNwxR11bMRK6TPjvn77CqfImvr5yMv4GXe4CjxIfbuT+hWm8faLCIbXAnfk1nCpv4hsr0wn0V+XrbtGhgdy/cDxvn6gg3wGT3+zKr+FoaQP/umKS6ko9Qrr7NnSbrTy97TyZieHcpWp/HuNrSycSEmDgqQ/O27WebrOVpz7IJy0mhDtV7c9jPLx8MhHGAJ58P9+u9ZgtVn7y4XkmxIay6QbV1XakdJfwX/q4hEt17Tx2U6Zq+/MgMWFBfGNVOjvza9h9fvT9tl/6uISLpja+d+tUdfTmQcaEBPCNVensL6q1q3z/euQyRTWt/Me6KQSo8h0xXe2x0to2nt1eyOqseNVzwwM9sHgCk+JC+cHWc7R3m0f89xWNHfxiZxErM+NZmamGyfA0X1ownvT4MJ546wwtnT0j/vuKxg5+8sF5lqTHsn666pkzGrpJ+Bar5NF/nCLQ4Md/b5yhxsz2QIH+WtmUNbTzX++ObBZMi1XyrVdPAPCDW6c5IzzFToH+fjx9dzbVzZ0jbtqxWiWPv3kaq4Qn71Df39HSTcJ/ett5DpfU871bp6ohcj3YwkkxfG3pJP52pIx3T1UO++9+tfsCR0rq+X+3Tyc1Rg1x7almp0bxoK18Xx/B8Mm/3F1EbqGJ/7wli3HRqnxHSxcJ/7W8Mv53XzH3zU/lnhw1hKqn+/e1GcxJjeSR105yqHjouW9fyyvjZzsLuXN2MnepE7Ue79trM1g0KYYn3jrD4WGU79aTlfx8ZxF3zknmi2pMJLv4dMKXUvLSRyX8xxunuHFyLN9Xh/peIcDgx4ub55EaHcKWl/PYea7/k3xSSl7+uJTH3zzNkvRYnrorWx3qewF/gx+//sIcxkUHc/8fjrBrgJnPpJT8/chlvvn349wwIVo15TiAkFK6O4Z+5eTkyLy8vFH9rZSS0xVNPLejkL0FJtZMTeB/7p2NMUD12fUmV5o62fLKUc5UNPOF+ak8tHQSqTEhSCk5V9XML3YWsf1cNauz4vnFptlqeFwvU9faxQMvHeVUeROfy0nh/yyfzITY0M+U75L0WJ7/Uo4a3nqYhBDHpJQ5/b7nawm/rL6dTc8foqKxg5BAA99eO4XNi9JUF0wv1dlj4cfv5/PXI9ow1jGhgXRbrLR0mgkL8ufhFZN4aOkkNXyCl2rvNvOLXUW8uL8Es1USGxaIxSppaO8hyN+PR9Zk8C83TlBdbEfAaQlfCBENvAqkAaXA56SUDf0slwq8AIwDJHCzlLJ0sHWPNuFbrJJ/f+0EiybHsiYrgajQwBGvQ/E8V5o6efdUJRdNrRj8BJmJEdyaPZYxIQHuDk1xgIrGDnacvUJ+VQsGg2BG8hjWTUskWn1/R8yZCf9poF5K+ZQQ4jEgSkr5aD/L7QV+JKXcIYQIA6xSykEnu7SnSUdRFEWvBkv49h4n3Q68bHv8MrCxn41PBfyllDsApJStQyV7RVEUxfHsTfgJUsoqANt9fD/LZACNQog3hRDHhRA/FUL0e/ZFCPGgECJPCJFnMpnsDE1RFEW53pDdGoQQO4H+rmN+YgTbWALMBi6jtfl/GXix74JSyueB50Fr0hnm+hVFUZRhGDLhSylXD/SeEKJaCJEkpawSQiQB/c1GXQ4cl1IW2/7mbWAB/SR8RVEUxXnsbdLZCmy2Pd4MvNPPMkeBKCFE72hlK4GRDZSiKIqi2M3ehP8UsEYIUQSssT1HCJEjhHgBQEppAb4N7BJCnAYE8Hs7t6soiqKMkF2XJkop64BV/byeB2y57vkOINuebSmKoij2UZevKYqi6ITHDq0ghDABl+xYRSxQ66BwHEnFNTKeGhd4bmwqrpHx1LhgdLGNl1L2O8OTxyZ8ewkh8ga62sydVFwj46lxgefGpuIaGU+NCxwfm2rSURRF0QmV8BVFUXTClxP+8+4OYAAqrpHx1LjAc2NTcY2Mp8YFDo7NZ9vwFUVRlE/z5Rq+oiiKch2V8BVFUXTC5xK+EGK9EKJACHHBNimLu+IYJ4TYI4TIF0KcFUL8X9vrPxBCVAghTthuN7spvlIhxGlbDHm216KFEDuEEEW2+ygXxzTluv1yQgjRLIT4pjv2mRDiD0KIGiHEmete63f/CM0vbZ+5U0KIOS6O66dCiPO2bb8lhIi0vZ4mhOi4br/9zllxDRLbgGUnhHjcts8KhBDrXBzXq9fFVCqEOGF73WX7bJAc4bzPmZTSZ26AAbgITAQCgZPAVDfFkgTMsT0OBwqBqcAPgG97wL4qBWL7vPY08Jjt8WPAT9xclleA8e7YZ8BSYA5wZqj9A9wMfIA2TtQC4LCL41qLNskQwE+uiyvt+uXctM/6LTvbd+EkEARMsH1vDa6Kq8/7zwLfc/U+GyRHOO1z5ms1/BuAC1LKYillN/B3tFm5XE5KWSWl/MT2uAXIB5LdEcsIDDmDmQutAi5KKe252nrUpJS5QH2flwfaP7cDr0jNISDSNly4S+KSUm6XUpptTw8BKc7Y9lAG2GcDuR34u5SyS0pZAlxA+/66NC4hhAA+B/zNGdsezCA5wmmfM19L+MlA2XXPy/GAJCuESEObAOaw7aWv2w7J/uDqZpPrSGC7EOKYEOJB22vDmcHMVTbx6S+hJ+yzgfaPJ33uvoJWC+w1QWgzze0TQixxU0z9lZ2n7LMlQLWUsui611y+z/rkCKd9znwt4Yt+bp7+7AAAAktJREFUXnNrv1OhTdr+D+CbUspm4LfAJGAWUIV2OOkOi6WUc4CbgH8VQix1UxyfIYQIBG4DXre95Cn7bCAe8bkTQjwBmIG/2F6qAlKllLOBR4C/CiEiXBzWQGXnEfsMuJdPVyxcvs/6yREDLtrPayPaZ76W8MuBcdc9TwEq3RQLQogAtIL8i5TyTQApZbWU0iKltKLNC+CUw9ihSCkrbfc1wFu2OKp7DxHFwDOYucJNwCdSympbjB6xzxh4/7j9cyeE2AxsAO6TtgZfW3NJne3xMbR28gxXxjVI2XnCPvMH7kSbdhVw/T7rL0fgxM+ZryX8o0C6EGKCrZa4CW1WLpeztQ2+CORLKZ+77vXr29zuAM70/VsXxBYqhAjvfYx20u8Mw5vBzBU+VevyhH1mM9D+2Qrcb+tFsQBo6j0kdwUhxHrgUeA2KWX7da/HCSEMtscTgXSg2FVx2bY7UNltBTYJIYKEEBNssR1xZWzAauC8lLK89wVX7rOBcgTO/Jy54my0K29oZ7IL0X6Zn3BjHDeiHW6dAk7YbjcDfwJO217fCiS5IbaJaD0kTgJne/cTEAPsAops99FuiC0EqAPGXPeay/cZ2g9OFdCDVrP6l4H2D9qh9q9tn7nTQI6L47qA1rbb+zn7nW3Zu2zlexL4BLjVDftswLIDnrDtswLgJlfGZXv9JeChPsu6bJ8NkiOc9jlTQysoiqLohK816SiKoigDUAlfURRFJ1TCVxRF0QmV8BVFUXRCJXxFURSdUAlfURRFJ1TCVxRF0Yn/HxQf36Dhoj7dAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "positions, velocities = [], []\n",
    "observation = env.reset()\n",
    "while True:\n",
    "    positions.append(observation[0])\n",
    "    velocities.append(observation[1])\n",
    "    next_observation, reward, done, _ = env.step(2)\n",
    "    if done:\n",
    "        break\n",
    "    observation = next_observation\n",
    "\n",
    "if next_observation[0] > 0.5:\n",
    "    print('成功到达')\n",
    "else:\n",
    "    print('失败退出')\n",
    "\n",
    "# 绘制位置和速度图像\n",
    "fig, ax = plt.subplots()\n",
    "ax.plot(positions, label='position')\n",
    "ax.plot(velocities, label='velocity')\n",
    "ax.legend()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "lLvraG9BC59H"
   },
   "source": [
    "### 线性近似最优策略求解\n",
    "砖瓦编码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "FV9A2ZMUC59J"
   },
   "outputs": [],
   "source": [
    "class TileCoder:\n",
    "    def __init__(self, layers, features):\n",
    "        self.layers = layers\n",
    "        self.features = features\n",
    "        self.codebook = {}\n",
    "    \n",
    "    def get_feature(self, codeword):\n",
    "        if codeword in self.codebook:\n",
    "            return self.codebook[codeword]\n",
    "        count = len(self.codebook)\n",
    "        if count >= self.features: # 冲突处理\n",
    "            return hash(codeword) % self.features\n",
    "        self.codebook[codeword] = count\n",
    "        return count\n",
    "    \n",
    "    def __call__(self, floats=(), ints=()):\n",
    "        dim = len(floats)\n",
    "        scaled_floats = tuple(f * self.layers * self.layers for f in floats)\n",
    "        features = []\n",
    "        for layer in range(self.layers):\n",
    "            codeword = (layer,) + tuple(int((f + (1 + dim * i) * layer) /\n",
    "                    self.layers) for i, f in enumerate(scaled_floats)) + ints\n",
    "            feature = self.get_feature(codeword)\n",
    "            features.append(feature)\n",
    "        return features"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "s005fHz9C59I"
   },
   "source": [
    "SARSA 算法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "zsB2g6FqC59N"
   },
   "outputs": [],
   "source": [
    "class SARSAAgent:\n",
    "    def __init__(self, env, layers=8, features=1893, gamma=1.,\n",
    "                learning_rate=0.03, epsilon=0.001):\n",
    "        self.action_n = env.action_space.n # 动作数\n",
    "        self.obs_low = env.observation_space.low\n",
    "        self.obs_scale = env.observation_space.high - \\\n",
    "                env.observation_space.low # 观测空间范围\n",
    "        self.encoder = TileCoder(layers, features) # 砖瓦编码器\n",
    "        self.w = np.zeros(features) # 权重\n",
    "        self.gamma = gamma # 折扣\n",
    "        self.learning_rate = learning_rate # 学习率\n",
    "        self.epsilon = epsilon # 探索\n",
    "        \n",
    "    def encode(self, observation, action): # 编码\n",
    "        states = tuple((observation - self.obs_low) / self.obs_scale)\n",
    "        actions = (action,)\n",
    "        return self.encoder(states, actions)\n",
    "    \n",
    "    def get_q(self, observation, action): # 动作价值\n",
    "        features = self.encode(observation, action)\n",
    "        return self.w[features].sum()\n",
    "    \n",
    "    def decide(self, observation): # 判决\n",
    "        if np.random.rand() < self.epsilon:\n",
    "            return np.random.randint(self.action_n)\n",
    "        else:\n",
    "            qs = [self.get_q(observation, action) for action in\n",
    "                    range(self.action_n)]\n",
    "            return np.argmax(qs)\n",
    "        \n",
    "    def learn(self, observation, action, reward,\n",
    "            next_observation, done, next_action): # 学习\n",
    "        u = reward + (1. - done) * self.gamma * \\\n",
    "                self.get_q(next_observation, next_action)\n",
    "        td_error = u - self.get_q(observation, action)\n",
    "        features = self.encode(observation, action)\n",
    "        self.w[features] += (self.learning_rate * td_error)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "ARu16qYxC59Q"
   },
   "outputs": [],
   "source": [
    "def play_sarsa(env, agent, train=False, render=False):\n",
    "    episode_reward = 0\n",
    "    observation = env.reset()\n",
    "    action = agent.decide(observation)\n",
    "    while True:\n",
    "        if render:\n",
    "            env.render()\n",
    "        next_observation, reward, done, _ = env.step(action)\n",
    "        episode_reward += reward\n",
    "        next_action = agent.decide(next_observation) # 终止状态时此步无意义\n",
    "        if train:\n",
    "            agent.learn(observation, action, reward, next_observation,\n",
    "                    done, next_action)\n",
    "        if done:\n",
    "            break\n",
    "        observation, action = next_observation, next_action\n",
    "    return episode_reward"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "KqZTOvMCC59U"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "平均回合奖励 = -11664.0 / 100 = -116.64\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "agent = SARSAAgent(env)\n",
    "\n",
    "# 训练\n",
    "episodes = 400\n",
    "episode_rewards = []\n",
    "for episode in range(episodes):\n",
    "    episode_reward = play_sarsa(env, agent, train=True)\n",
    "    episode_rewards.append(episode_reward)\n",
    "plt.plot(episode_rewards)\n",
    "\n",
    "# 测试\n",
    "agent.epsilon = 0. # 取消探索\n",
    "episode_rewards = [play_sarsa(env, agent) for _ in range(100)]\n",
    "print('平均回合奖励 = {} / {} = {}'.format(sum(episode_rewards),\n",
    "        len(episode_rewards), np.mean(episode_rewards)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "nytbD82PC59e"
   },
   "source": [
    "SARSA($\\lambda $)算法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "W4WB11kGC59f"
   },
   "outputs": [],
   "source": [
    "class SARSALambdaAgent(SARSAAgent):\n",
    "    def __init__(self, env, layers=8, features=1893, gamma=1.,\n",
    "            learning_rate=0.03, epsilon=0.001, lambd=0.9):\n",
    "        super().__init__(env=env, layers=layers, features=features,\n",
    "                gamma=gamma, learning_rate=learning_rate, epsilon=epsilon)\n",
    "        self.lambd = lambd\n",
    "        self.z = np.zeros(features) # 初始化资格迹\n",
    "        \n",
    "    def learn(self, observation, action, reward, next_observation, done,\n",
    "            next_action):\n",
    "        u = reward\n",
    "        if not done:\n",
    "            u += (self.gamma * self.get_q(next_observation, next_action))\n",
    "            self.z *= (self.gamma * self.lambd)\n",
    "            features = self.encode(observation, action)\n",
    "            self.z[features] = 1. # 替换迹\n",
    "        td_error = u - self.get_q(observation, action)\n",
    "        self.w += (self.learning_rate * td_error * self.z)\n",
    "        if done:\n",
    "            self.z = np.zeros_like(self.z) # 为下一回合初始化资格迹"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "z9W4PTfdC59i",
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "平均回合奖励 = -11130.0 / 100 = -111.3\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "agent = SARSALambdaAgent(env)\n",
    "\n",
    "# 训练\n",
    "episodes = 140\n",
    "episode_rewards = []\n",
    "for episode in range(episodes):\n",
    "    episode_reward = play_sarsa(env, agent, train=True)\n",
    "    episode_rewards.append(episode_reward)\n",
    "plt.plot(episode_rewards)\n",
    "\n",
    "# 测试\n",
    "agent.epsilon = 0. # 取消探索\n",
    "episode_rewards = [play_sarsa(env, agent) for _ in range(100)]\n",
    "print('平均回合奖励 = {} / {} = {}'.format(sum(episode_rewards),\n",
    "        len(episode_rewards), np.mean(episode_rewards)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "1ud7QDLyC59r"
   },
   "source": [
    "绘制价值和策略"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "5yPevgQCC59s"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1080x288 with 6 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "poses = np.linspace(env.unwrapped.min_position,\n",
    "          env.unwrapped.max_position, 128)\n",
    "vels = np.linspace(-env.unwrapped.max_speed, env.unwrapped.max_speed, 128)\n",
    "positions, velocities = np.meshgrid(poses, vels)\n",
    "\n",
    "# 绘制动作价值估计\n",
    "\n",
    "@np.vectorize\n",
    "def get_q(position, velocity, action):\n",
    "    return agent.get_q((position, velocity), action)\n",
    "\n",
    "q_values = np.empty((len(poses), len(vels), 3))\n",
    "for action in range(3):\n",
    "    q_values[:, :, action] = get_q(positions, velocities, action)\n",
    "\n",
    "\n",
    "fig, axes = plt.subplots(1, 3, figsize=(15, 4))\n",
    "for action, ax in enumerate(axes):\n",
    "    c = ax.pcolormesh(positions, velocities, q_values[:, :, action])\n",
    "    ax.set_xlabel('position')\n",
    "    ax.set_ylabel('velocity')\n",
    "    fig.colorbar(c, ax=ax)\n",
    "    ax.set_title('action = {}'.format(action))\n",
    "\n",
    "# 绘制状态价值估计\n",
    "\n",
    "v_values = q_values.max(axis=-1)\n",
    "\n",
    "fig, ax = plt.subplots(1, 1)\n",
    "c = ax.pcolormesh(positions, velocities, v_values)\n",
    "ax.set_xlabel('position')\n",
    "ax.set_ylabel('velocity')\n",
    "fig.colorbar(c, ax=ax);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "2BV6Ox_pC59x",
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXYAAAEGCAYAAABxfL6kAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAdQUlEQVR4nO3dfbQcdZ3n8ffHRJLhOSGAIQkGh6AbR8QxBGeZGdDwEOYcCaMBgjMaRzTqLnuGQeYQBhcQlA0cVmB32JU7GM2IAwIOh4xG7wmJYXY9PCQ8ExByCXpySQYkyaCAhKfv/FF1odLpvl23b3dXd/Xndc49t6vq11XfW9355tff/tWvFBGYmVl5vKPoAMzMrLmc2M3MSsaJ3cysZJzYzcxKxondzKxkxhYdQDvtpnExnj2KDmPEdkwbWczjNr3Uokiaa6R/VydpxTnulPPRLe+fVvst25+PiP0bff6JH90jtm57I1fb+x7e0R8Rcxs9VqWeSuzj2YOjNKfoMEbsqXM/MqL2v3/23S2KpLlG+nd1klac4045H93y/mm1O+LWX43m+Vu3vcG9/Qfnajtm8oZJozlWJZdizMxKpqd67Nb56vUWn7q6M3q1lTo1LutN7rGbmZWME7uZWcm4FNMFuuXLrP7ND+20fOJBHxy2/cBp1+268rR6R9n5GIfe/MUckQ2vahwVKo9TWXqp3Ecz4ipKvbJSt7wfe5l77GZmJePEbmZWMi7F9IDKEgnUL5NUe04zjttsecooRRynkfKOWbO4x25mVjJO7GZmJePEbmZWMoXW2CXNBa4BxgDXR8SSiu3jgH8EPgxsBU6PiF+m2w4HrgP2Bt4EjoyIV9oXfXcb6dBEa75WfD9QWbf30MTeVFiPXdIY4FrgJGAmcIakmRXNzgS2R8ShwFXA5elzxwI3AF+KiPcDxwKvtSl0M7OOVmQpZjYwEBEbI+JV4CZgXkWbecCy9PGtwBxJAk4AHo6IhwAiYmtE5Jsf08ys5IosxUwBNmWWB4GjarWJiNclvQDsBxwGhKR+YH/gpoi4otpBJC0CFgGMZ/em/gHdrLL00o6hidZ6Lr28bbTv6TGTmxRIAYpM7KqyLnK2GQv8MXAk8DKwStJ9EbFql8YRfUAfwN6aWLl/M7PSKbIUMwhMyyxPBTbXapPW1fcBtqXr74yI5yPiZWAF8Ictj9jMrAsUmdjXAjMkHSJpN2ABsLyizXJgYfp4PrA6IgLoBw6XtHua8I8BHmtT3GZmHa2wUkxaMz+LJEmPAZZGxHpJlwDrImI58G3ge5IGSHrqC9Lnbpf0TZL/HAJYERE/LuQPqVA5M14RNc88tUXX1Mup3uvqYa29odBx7BGxgqSMkl13YebxK8CpNZ57A8mQRzMzy/CVp2ZmJePZHZusFaUXl02sWVrxXnJ5p/O4x25mVjJO7GZmJePEbmZWMj1VY98xbQ+eOnf4G/VmNVIvrzfc0cPRzHbVjNlG/V3U29xjNzMrGSd2M7OS6alSTDuMdrijP05at8nznq1XWmlGCbLezcHbdePzTuAeu5lZyTixm5mVjEsxZtZ29co31coq7ZhQb+fjntvy4+UlaRrJ/Z/fRXKP576IuKZWeyd2M7PO9zrwlYi4X9JewH2SVkZE1enKXYoxM+twEbElIu5PH/8WeJzk1qFVObGbmXURSdOBDwH31GrTU6WYcZteavuNLzx80ayBK66v3rVN5VXdI1VvOGTBJklal1nuS+/XvBNJewI/BM6OiN/U2llPJXYzsw71fETMGq6BpHeSJPXvR8Q/D9fWpRgzsw4nSSS3Cn08Ir5Zr7177CPgsopZa4y2zNIDjgY+DTwi6cF03d+ltxfdhRO7mVmHi4j/Dyhve5dizMxKxondzKxkCi3FSJoLXAOMAa6PiCUV28eRXEb7YWArcHpE/DKz/WDgMeDiiLhyJMduxox0ZmadqLAeu6QxwLXAScBM4AxJMyuanQlsj4hDgauAyyu2XwX8pNWxmpl1kyJLMbOBgYjYGBGvAjcB8yrazAOWpY9vBeakw36QdAqwEVjfpnjNzLpCkaWYKcCmzPIgcFStNhHxuqQXgP0k/Q44DzieOlOwSVoELAIYz+5vrW+kzJLnOR4SaVZfh18F2vWK7LFXG7oTOdt8DbgqIl6sd5CI6IuIWREx652MayBMM7PuUmSPfRCYllmeCmyu0WZQ0lhgH2AbSc9+vqQrgH2BNyW9EhF/3/qwzcw6W5GJfS0wQ9IhwDPAAuBTFW2WAwuBu4D5wOqICOBPhhpIuhh40UndzCxRWGJPa+ZnAf0kwx2XRsR6SZcA6yJiOcncCN+TNEDSU1/QzhhdLzdrjcobS3dKzT0b15i/LjCQUSp0HHs6z8GKinUXZh6/ApxaZx8XtyQ4M7Mu5StPzcxKxpOAmVnhOqU006k3sx4p99jNzErGid3MrGSc2M3MSsaJ3cysZJzYzcxKxondzKxkPNzRzDpO5fDHaloxJLIsV566x25mVjJO7GZmJeNSTIYn/TLrHnnKNb3KPXYzs5JxYjczKxkndjOzknFiNzMrGSd2M7OScWI3MysZD3c0s55w4kEf3GVdWYc4u8duZlYy7rGbmbXAI9v3H8F8Ns29DV/PJvayfgSz1qj2MX44fn91nl56TQotxUiaK+kJSQOSFlfZPk7SD9Lt90ianq4/XtJ9kh5Jf3+s3bGbmXWqwhK7pDHAtcBJwEzgDEkzK5qdCWyPiEOBq4DL0/XPAx+PiA8AC4HvtSdqM7POV2SPfTYwEBEbI+JV4CZgXkWbecCy9PGtwBxJiogHImJzun49MF7SuLZEbWbW4YqssU8BNmWWB4GjarWJiNclvQDsR9JjH/JJ4IGI2FHtIJIWAYsADp4ylv51vVNns8aMtJ6eZx+9VN+14hXZY1eVdTGSNpLeT1KeqfnVc0T0RcSsiJi1/35jGgrUzKybFJnYB4FpmeWpwOZabSSNBfYBtqXLU4HbgM9ExFMtj9bMrEsUWYpZC8yQdAjwDLAA+FRFm+UkX47eBcwHVkdESNoX+DFwfkT8vI0xWwk1o/TSyDFcnrFWKazHHhGvA2cB/cDjwM0RsV7SJZJOTpt9G9hP0gBwDjA0JPIs4FDgv0t6MP05oM1/gplZRyr0AqWIWAGsqFh3YebxK8CpVZ73deDrLQ/QzKwLea4YM7OS6dkpBcyK5iGR1irusZuZlYwTu5lZybgUY6XXjuGMZp3EPXYzs5JxYjczKxkndjOzkqlbY5d0JfCdiFjfhnjMepaHP1qz5Omx/wLoS+9g9CVJ+7Q6KDMza1zdxB4R10fE0cBngOnAw5L+SdJHWx2cmZmNXK7hjult7N6X/jwPPAScI+mLEbGghfGZjVhZhjfmucP9wGnXjeg5le2tnPLU2L8JfBxYDVwWEfemmy6X9EQrgzMzs5HL02N/FPhqRLxcZdvsJsdjZmajlCex/0VELM2ukLQqIuZExAstisusqrKUWZolT7kmyyNvekPNxC5pPLA7MEnSBN6+/+jewEFtiM3MzBowXI/9i8DZJEn8/sz63wDXtjIoMzNrXM3EHhHXANdI+m8R8b/bGJOZmY3CcKWYj0XEauAZSZ+o3B4R/9zSyMxwTb2ekQ53fOrqj1S0/8gubeoNiXSdvvMNV4o5hmSI48erbAvAid3MrAMNV4q5KP39V+0Lx8zMRivPBUqXAVdExL+nyxOAr0TEV1sdnJkNb6TDHZuyz6sr24+8nGOtlWcSsJOGkjpARGwH/qwZB5c0V9ITkgYkLa6yfZykH6Tb75E0PbPt/HT9E5JObEY8ZmadSNJSSc9JejRP+zyJfYykcZkD/B4wbpj2uaTzz1wLnATMBM6QNLOi2ZnA9og4FLgKuDx97kxgAfB+YC7wf9L9mZmV0XdJcl0ueRL7DcAqSWdK+hywEljWWGw7mQ0MRMTGiHgVuAmYV9FmXuZYtwJzJCldf1NE7IiIp4EBPL2BmZVURPwrsC1v+7o19oi4QtLDwHHpqksjor/B+LKmAJsyy4PAUbXaRMTrkl4A9kvX313x3CnVDiJpEbAI4OApvnd3p/PwxnKorNN3Qs292nurcvjn75+dTSsbWhzRTiZJWpdZ7ouIvkZ3ljfTPQC8k2SY4wONHqyCqqyLnG3yPDdZmZycPoBZHxxftY2ZWcGej4hZzdpZ3VKMpNOAe4H5wGnAPZLmN+HYg8C0zPJUYHOtNpLGAvuQfBzJ81wzs56Up8d+AXBkRDwHIGl/4A6SmvdorAVmSDoEeIbky9BPVbRZDiwE7iL5j2V1RISk5cA/pXPFHwTMIPnPx8y6UGXpZueSSHXDl1F6W54vT98xlNRTW3M+b1gR8TpwFtAPPA7cHBHrJV0i6eS02beB/SQNAOcAi9PnrgduBh4Dfgr814h4Y7QxmZl1Ikk3knRw3ytpUNKZw7XP02P/qaR+4MZ0+XRgxejCTETEisp9RcSFmcevAKfWeO43gG80Iw4zs04WEWeMpH2eUTF/K+mTwNEkX1r2RcRtDcZnZmYtlmtUTET8EPhhi2OxkvHQRRsy0qkPKuvnjejlGvxw0/b+lupDCAVEROzdsqjMzKxhw83uuFc7AzEzs+bIVYqR9MfAjIj4jqRJwF7ppfzWpVwm6XyNXK3ZitkeO1W90kqe0ktZyzN5LlC6CDgPOD9dtRvJ/DFmZtaB8oxH/3PgZOAlgIjYDLhMY2bWofKUYl5Nr/YMAEl7tDgmazKXXXpXZamhGaNNWmGk926F+vda7eX3fZ4e+82SrgP2lfQFkukE/qG1YZmZWaPy9NjfBP4f8BvgMODCiFjZ0qjMzKxheRL7XiR3MtpGcjOMh1sakZmZjUqeKQW+BnxN0uEk88TcKWkwIo6r81Rrk16uJZZJvZpxNfVq0Z1aU6/US8M022EkszQ+B/wbyeyOB7QmHDMzG60849i/LGkNsAqYBHwhIg5vdWBmZtaYPDX2dwNnR8SDrQ7G8nHppZwqX9dGSjP2tsrz10v/bvLU2Be3IxAzM2uOUd8JyczMOosTu5lZyeSa3dHMOlMjl+IXoV6cjcxkabW5x25mVjJO7GZmJeNSTBfopWFa9rY8r3u3DIns1BJRWRXSY5c0UdJKSRvS3xNqtFuYttkgaWG6bndJP5b0C0nrJS1pb/RmZp2tqFLMYmBVRMwguaJ1l7HykiYCFwFHAbOBizL/AVwZEe8DPgQcLemk9oRtZtb5ikrs84Bl6eNlwClV2pwIrIyIbRGxHVgJzI2IlyPiZwAR8SpwPzC1DTGbmXWFomrsB0bEFoCI2CKp2qRiU4BNmeXBdN1bJO0LfBy4ptaBJC0CFgGMZ/e36padWpt0Pd2GdOp7tFv08r+lliV2SXcA76qy6YK8u6iyLjL7HwvcCPyviNhYaycR0Qf0AeytiVGrnZlZWbQssQ83X7ukZyVNTnvrk0mmBK40CBybWZ4KrMks9wEbIuLqJoRrZlYaRZVilgMLgSXp79urtOkHLst8YXoCcD6ApK8D+wCfH8lBDzv8Zfr7O+vjbS9/XOx1lTfBqLzxdDcPEfSVpMUq6svTJcDxkjYAx6fLSJol6XqAiNgGXAqsTX8uiYhtkqaSlHNmAvdLelDSiBK8mVmZFdJjj4itwJwq69eR6YVHxFJgaUWbQarX383MjB678vSR7fu/9fG22kfFdtzowKUXs94wbtNLu5TXanm6ycf2XDFmZiXjxG5mVjJO7GZmJdNTNfZ6fKWftUrl0MZG25RFK77P8vdXb3OP3cysZJzYzcxKpqdKMR+Y8GvuTYc5NuNjW7WPj/44aGZFc4/dzKxknNjNzErGid3MrGR6qsbeDpV1d9fcrRf5fV8s99jNzErGid3MrGRcihkFf9y0olXOUtrNN+fI8r+t0XGP3cysZJzYzcxKxondzKxkeqrG/uTDu7t2Z9aB/O+yudxjNzMrGSd2M7OS6alSzGGHv0x/f3JlqD/6WTvlvalxVi/deMOaq5Aeu6SJklZK2pD+nlCj3cK0zQZJC6tsXy7p0dZHbGbWPYoqxSwGVkXEDGBVurwTSROBi4CjgNnARdn/ACR9AnixPeGamXWPokox84Bj08fLgDXAeRVtTgRWRsQ2AEkrgbnAjZL2BM4BFgE35z2oR8VYN6ks3+QpzVReiVpPq65UrRdrI6Upy6+oHvuBEbEFIP19QJU2U4BNmeXBdB3ApcD/BF6udyBJiyStk7TuNXaMLmozsy7Qsh67pDuAd1XZdEHeXVRZF5KOAA6NiL+RNL3eTiKiD+gD2FsTI+exzcy6VssSe0QcV2ubpGclTY6ILZImA89VaTbI2+UagKkkJZs/Aj4s6Zck8R8gaU1EHIuZmRVWY18OLASWpL9vr9KmH7gs84XpCcD5ac39/wKkPfYflSmpV6tNuh5pMPL6eaP7LMsMkb2sqBr7EuB4SRuA49NlJM2SdD1AmsAvBdamP5cMfZFqZma1FdJjj4itwJwq69cBn88sLwWWDrOfXwJ/0IIQzcy6Vk9dedoNXHbZWS/fQ7byb2+XThkyaY3zXDFmZl1A0lxJT0gakLTLRZ1ZTuxmZh1O0hjgWuAkYCZwhqSZtdo7sZuZdb7ZwEBEbIyIV4GbSK7gr8o1ditMtRpyL9XQ66k8F0XV3K0jVLsS/6hajZ3YzcyKN0nSusxyX3rV/JCqV+LX2pkTu5lZ8Z6PiFnDbB8EpmWWpwKbazV2Yu8wea48rWzTrUMkW1V26dbz062llm45v11uLTBD0iHAM8AC4FO1Gjuxm5l1uIh4XdJZJFOtjAGWRsT6Wu2d2M3MukBErABW5GnrxN5hqn2sHem9Lys/0jdyZWA7Pl63qvRQRGmgW8sojfDIpc7ncexmZiXjxG5mVjJO7GZmJeMae4cZaT0dGqvveoja6PRSTd26j3vsZmYl48RuZlYyLsV0mEbua9nIcMZGSj711CvvNKN80cs33jDLyz12M7OScWI3MysZJ3Yzs5Jxjb1gzag716vLt+tmwx4C2H38HUU5FdJjlzRR0kpJG9LfE2q0W5i22SBpYWb9bpL6JD0p6ReSPtm+6M3MOltRpZjFwKqImAGsSpd3ImkicBHJ7Z9mAxdl/gO4AHguIg4jubHrnW2J2sysCxRVipkHHJs+XgasAc6raHMisDIitgFIWgnMBW4EPge8DyAi3gSez3PQHdP24Klzk2F+vXTlZSNDKDuVSwfDq1d266X3fS8rqsd+YERsAUh/H1ClTbWbt06RtG+6fKmk+yXdIunAWgeStEjSOknr3njxpWbFb2bWsVqW2CXdIenRKj/z8u6iyrog+ZQxFfh5RPwhcBdwZa2dRERfRMyKiFlj9txjxH+HmVm3aVkpJiKOq7VN0rOSJkfEFkmTgeeqNBvk7XINJMl8DbAVeBm4LV1/C3BmM2I2MyuDomrsy4GFwJL09+1V2vQDl2W+MD0BOD8iQtK/kCT91cAc4LGRBpDnkvpurUdW1lnLVGO3nbVrKKt1l6Jq7EuA4yVtAI5Pl5E0S9L1AOmXppeS3J17LXDJ0BepJF+0XizpYeDTwFfaHL+ZWccqpMceEVtJetqV69cBn88sLwWWVmn3K+BPWxmjmVm38pWnw6hXrmmkVNOKqzNb8XHc5Ryz7uW5YszMSsaJ3cysZHqqFDNu00tdO9Kl3Tq19JKnlNVLV6f6/WzVuMduZlYyTuxmZiXjxG5mVjI9VWMvwq5DJps/3LFT6+Fl1kt1fOs+7rGbmZWME7uZWckoIoqOoW0k/Rr4VZsPO4mcNwJps06MyzHl45jyG01c746I/Rs9sKSfpsfP4/mImNvosXY5di8l9iJIWhcRs4qOo1InxuWY8nFM+XVqXK3mUoyZWck4sZuZlYwTe+v1FR1ADZ0Yl2PKxzHl16lxtZRr7GZmJeMeu5lZyTixm5mVjBN7E0g6VdJ6SW9Kqjm0StJcSU9IGpC0OLP+EEn3SNog6QeSdmtCTBMlrUz3uTJzU/Bsm49KejDz84qkU9Jt35X0dGbbEaONKW9cabs3Msdenllf1Lk6QtJd6ev8sKTTM9uadq5qvUcy28elf/dAeh6mZ7adn65/QtKJjcbQQEznSHosPS+rJL07s63q69iGmD4r6deZY38+s21h+lpvkLSwWTF1lIjwzyh/gP8EvBdYA8yq0WYM8BTwHmA3kkljZqbbbgYWpI+/BXy5CTFdASxOHy8GLq/TfiKwDdg9Xf4uML8F5ypXXMCLNdYXcq6Aw4AZ6eODgC3Avs08V8O9RzJt/gvwrfTxAuAH6eOZaftxwCHpfsa0KaaPZt43Xx6KabjXsQ0xfRb4+xrv843p7wnp4wnNjrHoH/fYmyAiHo+IJ+o0mw0MRMTGiHgVuAmYJ0nAx4Bb03bLgFOaENa8dF959zkf+ElEvNyEYw9npHG9pchzFRFPRsSG9PFm4Dmg4asSa6j6Hhkm1luBOel5mQfcFBE7IuJpYCDdX8tjioifZd43dwNTm3DcUcU0jBOBlRGxLSK2AyuBpl3x2Smc2NtnCrApszyYrtsP+PeIeL1i/WgdGBFbANLfB9RpvwC4sWLdN9KP11dJGteEmEYS13hJ6yTdPVQeokPOlaTZJD3FpzKrm3Guar1HqrZJz8MLJOclz3NbFVPWmcBPMsvVXsd2xfTJ9DW5VdK0ET63q3na3pwk3QG8q8qmCyLi9jy7qLIuhlk/qpjyPD+zn8nAB4D+zOrzgX8jSWB9wHnAJW2M6+CI2CzpPcBqSY8Av6nSrohz9T1gYUS8ma5u+FxV7r7Kusq/r+nvozpy71fSXwKzgGMyq3d5HSPiqWrPb3JM/wLcGBE7JH2J5FPOx3I+t+s5secUEceNcheDwLTM8lRgM8kERftKGpv2wIbWjyomSc9KmhwRW9Jk9NwwuzoNuC0iXsvse0v6cIek7wDn5ompWXGl5Q4iYqOkNcCHgB9S4LmStDfwY+CrEfHWzUZHc64q1HqPVGszKGkssA/JdyN5ntuqmJB0HMl/ksdExI6h9TVex9Em9roxRcTWzOI/AJdnnntsxXPXjDKejuNSTPusBWakozp2Iyl9LI/kG52fkdS4ARYCeT4B1LM83VeefZ5BRRkmTXBDde1TgEebEFOuuCRNGCpnSJoEHA08VuS5Sl+z24B/jIhbKrY161xVfY8ME+t8YHV6XpYDC9JRM4cAM4B7G4xjRDFJ+hBwHXByRDyXWV/1dWxTTJMziycDj6eP+4ET0tgmACew8yfVcij629sy/AB/TtIT2AE8C/Sn6w8CVmTa/RnwJEmP5YLM+veQ/CMcAG4BxjUhpv2AVcCG9PfEdP0s4PpMu+nAM8A7Kp6/GniEJEndAOzZpHNVNy7gP6fHfij9fWbR5wr4S+A14MHMzxHNPlfV3iMkZZ2T08fj0797ID0P78k894L0eU8AJzXx/V0vpjvS9/3QeVle73VsQ0z/A1ifHvtnwPsyz/1cev4GgL9qVkyd9OMpBczMSsalGDOzknFiNzMrGSd2M7OScWI3MysZJ3Yzs5JxYreeJelLkj6TPv6spIMy266XNLO46Mwa5+GOZkB6VeS5EbGu6FjMRss9dutKkqZL+oWkZZmJnnaXNEfSA5IekbQ0c+Xjksyc4Vem6y6WdK6k+SQXI30/nbv79yStUTq3vqQz0v09KunyTAwvSvqGpIfSSa4OLOJcmFVyYrdu9l6gLyIOJ5kg7BySudFPj4gPkMyF9GVJE0muDn5/2vbr2Z1ExK3AOuAvIuKIiPjd0La0PHM5yQRSRwBHZmYp3AO4OyI+CPwr8IWW/aVmI+DEbt1sU0T8PH18AzAHeDoinkzXLQP+lCTpvwJcL+kTwEjmnD8SWBMRv45k4rHvp/sEeBX4Ufr4PpLpGcwK58Ru3SzXF0RpQp5NMjvkKcBPR3CMatO8Dnkt3v6S6g08W6p1CCd262YHS/qj9PEZJJNRTZd0aLru08CdkvYE9omIFcDZJCWVSr8F9qqy/h7gGEmTJI1Jj3NnM/8Is2ZzD8O62ePAQknXkczM+Nckt2a7JZ2rfC3JfVEnArdLGk/SA/+bKvv6LvAtSb8Dhv6zIJI52s8nmSFQJLN1NmOqYLOW8XBH60qSpgM/iog/KDgUs47jUoyZWcm4x25mVjLusZuZlYwTu5lZyTixm5mVjBO7mVnJOLGbmZXMfwA7fTF1CaNWCQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 绘制动作估计\n",
    "\n",
    "@np.vectorize\n",
    "def decide(position, velocity):\n",
    "    return agent.decide((position, velocity))\n",
    "\n",
    "q_values = np.empty((len(poses), len(vels), 3))\n",
    "action_values = decide(positions, velocities)\n",
    "\n",
    "fig, ax = plt.subplots()\n",
    "c = ax.pcolormesh(positions, velocities, action_values)\n",
    "ax.set_xlabel('position')\n",
    "ax.set_ylabel('velocity')\n",
    "fig.colorbar(c, ax=ax, boundaries=[-.5, .5, 1.5, 2.5], ticks=[0, 1, 2]);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 深度 Q 网络求解最优策略"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "用于画图的类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib notebook\n",
    "\n",
    "\n",
    "class Chart:\n",
    "    def __init__(self):\n",
    "        self.fig, self.ax = plt.subplots(1, 1)\n",
    "        # plt.ion()\n",
    "    \n",
    "    def plot(self, episode_rewards):\n",
    "        self.ax.clear()\n",
    "        self.ax.plot(episode_rewards)\n",
    "        self.ax.set_xlabel('iteration')\n",
    "        self.ax.set_ylabel('episode reward')\n",
    "        self.fig.canvas.draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "eE_8O-htDrnl"
   },
   "source": [
    "经验回放"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "TsOqN8VJC592"
   },
   "outputs": [],
   "source": [
    "class DQNReplayer:\n",
    "    def __init__(self, capacity):\n",
    "        self.memory = pd.DataFrame(index=range(capacity),\n",
    "                columns=['observation', 'action', 'reward',\n",
    "                'next_observation', 'done'])\n",
    "        self.i = 0\n",
    "        self.count = 0\n",
    "        self.capacity = capacity\n",
    "    \n",
    "    def store(self, *args):\n",
    "        self.memory.loc[self.i] = args\n",
    "        self.i = (self.i + 1) % self.capacity\n",
    "        self.count = min(self.count + 1, self.capacity)\n",
    "        \n",
    "    def sample(self, size):\n",
    "        indices = np.random.choice(self.count, size=size)\n",
    "        return (np.stack(self.memory.loc[indices, field]) for field in\n",
    "                self.memory.columns)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "N6fehATMETDo"
   },
   "source": [
    "DQN"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "ZAMDYhvaEPnQ"
   },
   "outputs": [],
   "source": [
    "class DQNAgent:\n",
    "    def __init__(self, env, net_kwargs={}, gamma=0.99, epsilon=0.001,\n",
    "             replayer_capacity=10000, batch_size=64):\n",
    "        observation_dim = env.observation_space.shape[0]\n",
    "        self.action_n = env.action_space.n\n",
    "        self.gamma = gamma\n",
    "        self.epsilon = epsilon\n",
    "        \n",
    "        self.batch_size = batch_size\n",
    "        self.replayer = DQNReplayer(replayer_capacity) # 经验回放\n",
    "         \n",
    "        self.evaluate_net = self.build_network(input_size=observation_dim,\n",
    "                output_size=self.action_n, **net_kwargs) # 评估网络\n",
    "        self.target_net = self.build_network(input_size=observation_dim,\n",
    "                output_size=self.action_n, **net_kwargs) # 目标网络\n",
    "\n",
    "        self.target_net.set_weights(self.evaluate_net.get_weights())\n",
    "\n",
    "    def build_network(self, input_size, hidden_sizes, output_size,\n",
    "                activation=tf.nn.relu, output_activation=None,\n",
    "                learning_rate=0.01): # 构建网络\n",
    "        model = keras.Sequential()\n",
    "        for layer, hidden_size in enumerate(hidden_sizes):\n",
    "            kwargs = dict(input_shape=(input_size,)) if not layer else {}\n",
    "            model.add(keras.layers.Dense(units=hidden_size,\n",
    "                    activation=activation, **kwargs))\n",
    "        model.add(keras.layers.Dense(units=output_size,\n",
    "                activation=output_activation)) # 输出层\n",
    "        optimizer = tf.optimizers.Adam(lr=learning_rate)\n",
    "        model.compile(loss='mse', optimizer=optimizer)\n",
    "        return model\n",
    "        \n",
    "    def learn(self, observation, action, reward, next_observation, done):\n",
    "        self.replayer.store(observation, action, reward, next_observation,\n",
    "                done) # 存储经验\n",
    "\n",
    "        observations, actions, rewards, next_observations, dones = \\\n",
    "                self.replayer.sample(self.batch_size) # 经验回放\n",
    "\n",
    "        next_qs = self.target_net.predict(next_observations)\n",
    "        next_max_qs = next_qs.max(axis=-1)\n",
    "        us = rewards + self.gamma * (1. - dones) * next_max_qs\n",
    "        targets = self.evaluate_net.predict(observations)\n",
    "        targets[np.arange(us.shape[0]), actions] = us\n",
    "        self.evaluate_net.fit(observations, targets, verbose=0)\n",
    "\n",
    "        if done: # 更新目标网络\n",
    "            self.target_net.set_weights(self.evaluate_net.get_weights())\n",
    "\n",
    "    def decide(self, observation): # epsilon贪心策略\n",
    "        if np.random.rand() < self.epsilon:\n",
    "            return np.random.randint(self.action_n)\n",
    "        qs = self.evaluate_net.predict(observation[np.newaxis])\n",
    "        return np.argmax(qs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "cowmxSvsFZCK"
   },
   "outputs": [],
   "source": [
    "def play_qlearning(env, agent, train=False, render=False):\n",
    "    episode_reward = 0\n",
    "    observation = env.reset()\n",
    "    while True:\n",
    "        if render:\n",
    "            env.render()\n",
    "        action = agent.decide(observation)\n",
    "        next_observation, reward, done, _ = env.step(action)\n",
    "        episode_reward += reward\n",
    "        if train:\n",
    "            agent.learn(observation, action, reward, next_observation,\n",
    "                    done)\n",
    "        if done:\n",
    "            break\n",
    "        observation = next_observation\n",
    "    return episode_reward"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "aDcTjATVEcRQ"
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support. ' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        fig.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option);\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"\" width=\"432\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "平均回合奖励 = -13425.0 / 100 = -134.25\n"
     ]
    },
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support. ' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        fig.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option);\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"\" width=\"432\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "net_kwargs = {'hidden_sizes' : [64, 64], 'learning_rate' : 0.001}\n",
    "agent = DQNAgent(env, net_kwargs=net_kwargs)\n",
    "\n",
    "# 训练\n",
    "episodes = 500\n",
    "episode_rewards = []\n",
    "chart = Chart()\n",
    "for episode in range(episodes):\n",
    "    episode_reward = play_qlearning(env, agent, train=True)\n",
    "    episode_rewards.append(episode_reward)\n",
    "    chart.plot(episode_rewards)\n",
    "\n",
    "# 测试\n",
    "agent.epsilon = 0. # 取消探索\n",
    "episode_rewards = [play_qlearning(env, agent) for _ in range(100)]\n",
    "print('平均回合奖励 = {} / {} = {}'.format(sum(episode_rewards),\n",
    "        len(episode_rewards), np.mean(episode_rewards)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "u8xoo8xGEcsa"
   },
   "source": [
    "双重深度 Q 网络"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "XJkuKVHEEXeR"
   },
   "outputs": [],
   "source": [
    "class DoubleDQNAgent(DQNAgent):\n",
    "    def learn(self, observation, action, reward, next_observation, done):\n",
    "        self.replayer.store(observation, action, reward, next_observation,\n",
    "                done) # 存储经验\n",
    "        observations, actions, rewards, next_observations, dones = \\\n",
    "                self.replayer.sample(self.batch_size) # 经验回放\n",
    "        next_eval_qs = self.evaluate_net.predict(next_observations)\n",
    "        next_actions = next_eval_qs.argmax(axis=-1)\n",
    "        next_qs = self.target_net.predict(next_observations)\n",
    "        next_max_qs = next_qs[np.arange(next_qs.shape[0]), next_actions] \n",
    "        us = rewards + self.gamma * next_max_qs * (1. - dones)\n",
    "        targets = self.evaluate_net.predict(observations)\n",
    "        targets[np.arange(us.shape[0]), actions] = us\n",
    "        self.evaluate_net.fit(observations, targets, verbose=0)\n",
    "\n",
    "        if done:\n",
    "            self.target_net.set_weights(self.evaluate_net.get_weights())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "42nFQnMpEq1u"
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support. ' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        fig.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option);\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"\" width=\"432\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "平均回合奖励 = -15188.0 / 100 = -151.88\n"
     ]
    },
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support. ' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        fig.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option);\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"\" width=\"432\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "net_kwargs = {'hidden_sizes' : [64, 64], 'learning_rate' : 0.001}\n",
    "agent = DoubleDQNAgent(env, net_kwargs=net_kwargs)\n",
    "\n",
    "# 训练\n",
    "episodes = 500\n",
    "episode_rewards = []\n",
    "chart = Chart()\n",
    "for episode in range(episodes):\n",
    "    episode_reward = play_qlearning(env, agent, train=True)\n",
    "    episode_rewards.append(episode_reward)\n",
    "    chart.plot(episode_rewards)\n",
    "\n",
    "# 测试\n",
    "agent.epsilon = 0. # 取消探索\n",
    "episode_rewards = [play_qlearning(env, agent) for _ in range(100)]\n",
    "print('平均回合奖励 = {} / {} = {}'.format(sum(episode_rewards),\n",
    "        len(episode_rewards), np.mean(episode_rewards)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "env.close()"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "MountainCar_v0_201901302220.ipynb",
   "provenance": [],
   "version": "0.3.2"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
